package com.chorye.function.schedule.service;

import com.chorye.function.schedule.common.QuartzConst;
import com.chorye.function.schedule.entity.QuartzEntity;
import com.chorye.function.schedule.entity.QuartzResp;
import com.chorye.function.schedule.exception.QuartzException;
import com.chorye.function.schedule.mapper.QuartzMapper;
import com.chorye.function.schedule.util.JobUtil;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.quartz.CronScheduleBuilder.cronSchedule;

/**
 * 定时任务的服务层
 *
 * @author Chorye
 */
@Service
public class QuartzService {

    /**
     * 日志工具
     */
    private final Logger log = LoggerFactory.getLogger(QuartzService.class);
    /**
     * 定时任务的持久层
     */
    @Autowired
    private QuartzMapper quartzMapper;
    /**
     * 定时调度器工厂Bean，由这个Bean来操作定时任务
     */
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    /**
     * 查询所有的定时任务列表
     *
     * @param pageNo   当前页数
     * @param pageSize 每页条数
     * @return 定时任务列表
     */
    public Map<String, Object> list(Integer pageNo, Integer pageSize) {
        // 查询所有任务
        List<QuartzEntity> list = quartzMapper.listAll((pageNo - 1) * pageSize, pageSize);
        // 设置时间
        list.forEach(quartzEntity -> {
            quartzEntity.setStartTime(JobUtil.fmtLongTimeToString(quartzEntity.getStartTime()));
            quartzEntity.setEndTime(JobUtil.fmtLongTimeToString(quartzEntity.getEndTime()));
            quartzEntity.setNextFireTime(JobUtil.fmtLongTimeToString(quartzEntity.getNextFireTime()));
            quartzEntity.setPrevFireTime(JobUtil.fmtLongTimeToString(quartzEntity.getPrevFireTime()));
        });
        // 返回
        Map<String, Object> jsonMap = new HashMap<>(4);
        jsonMap.put("code", QuartzConst.ZERO_CHAR);
        jsonMap.put("msg", "");
        jsonMap.put("count", quartzMapper.count());
        jsonMap.put("data", list);
        return jsonMap;
    }

    /**
     * 操作定时任务，根据不同任务类型进行分发处理
     *
     * @param quartzEntity 定时任务实体类
     * @return 操作的响应
     */
    public QuartzResp dispatcher(QuartzEntity quartzEntity) {
        // 操作的类型
        String operate = quartzEntity.getOperate();
        // 初始化响应信息
        QuartzResp resp;
        // 根据不同的操作类型交给不同的方法进行处理
        if (QuartzConst.OPERATE_TYPE_PAUSE.equals(operate)) {
            // 暂停操作
            resp = handlePause(quartzEntity);
        } else if (QuartzConst.OPERATE_TYPE_RESUME.equals(operate)) {
            // 恢复操作
            resp = handleResume(quartzEntity);
        } else if (QuartzConst.OPERATE_TYPE_DELETE.equals(operate)) {
            // 删除操作
            resp = handleDelete(quartzEntity);
        } else if (QuartzConst.OPERATE_TYPE_SAVE.equals(operate)) {
            // 新增操作
            resp = handleSave(quartzEntity);
        } else {
            // 设置失败的响应信息
            resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.OPERATE_MSG_ERROR);
        }
        // 返回
        return resp;
    }

    /**
     * 新增操作处理逻辑
     *
     * @param quartzEntity 定时任务实体
     * @return 响应信息
     */
    private QuartzResp handleSave(QuartzEntity quartzEntity) {
        QuartzResp resp;
        try {
            // 处理
            doSave(quartzEntity);
            // 设置成功的响应信息
            resp = new QuartzResp(QuartzConst.SUCCESS, QuartzConst.OPERATE_MSG_SAVE_SUCCESS);
        } catch (ClassNotFoundException | SchedulerException e) {
            // 设置失败的响应信息
            resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.OPERATE_MSG_SAVE_FAILURE);
        } catch (QuartzException e) {
            // 设置失败的响应信息
            resp = new QuartzResp(QuartzConst.ERROR, e.getMessage());
            // 打印错误信息
            if (log.isErrorEnabled()) {
                log.error(e.toString());
            }
        }
        return resp;
    }

    /**
     * 删除操作处理逻辑
     *
     * @param quartzEntity 定时任务实体
     * @return 响应信息
     */
    private QuartzResp handleDelete(QuartzEntity quartzEntity) {
        QuartzResp resp;
        try {
            // 处理
            doDelete(quartzEntity);
            // 设置成功的响应信息
            resp = new QuartzResp(QuartzConst.SUCCESS, QuartzConst.OPERATE_MSG_DELETE_SUCCESS);
        } catch (SchedulerException e) {
            // 设置失败的响应信息
            resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.OPERATE_MSG_DELETE_FAILURE);
            // 打印错误信息
            if (log.isErrorEnabled()) {
                log.error(e.toString());
            }
        }
        return resp;
    }

    /**
     * 恢复操作处理逻辑
     *
     * @param quartzEntity 定时任务实体
     * @return 响应信息
     */
    private QuartzResp handleResume(QuartzEntity quartzEntity) {
        QuartzResp resp;
        try {
            // 处理
            doResume(quartzEntity);
            // 设置成功的响应信息
            resp = new QuartzResp(QuartzConst.SUCCESS, QuartzConst.OPERATE_MSG_RESUME_SUCCESS);
        } catch (SchedulerException e) {
            // 设置失败的响应信息
            resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.OPERATE_MSG_RESUME_FAILURE);
            // 打印错误信息
            if (log.isErrorEnabled()) {
                log.error(e.toString());
            }
        }
        return resp;
    }

    /**
     * 暂停操作处理逻辑
     *
     * @param quartzEntity 定时任务实体
     * @return 响应信息
     */
    private QuartzResp handlePause(QuartzEntity quartzEntity) {
        QuartzResp resp;
        try {
            // 处理
            doPause(quartzEntity);
            // 设置成功的响应信息
            resp = new QuartzResp(QuartzConst.SUCCESS, QuartzConst.OPERATE_MSG_PAUSE_SUCCESS);
        } catch (SchedulerException e) {
            // 设置错误的响应信息
            resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.OPERATE_MSG_PAUSE_FAILURE);
            // 打印错误信息
            if (log.isErrorEnabled()) {
                log.error(e.toString());
            }
        }
        return resp;
    }

    /**
     * 检查任务的名称、触发器的名称是否被占用
     *
     * @param quartzEntity 定时任务实体
     * @return 检查结果
     */
    public QuartzResp checkName(QuartzEntity quartzEntity) {
        // 取出任务名称和触发器名称
        String jobName = quartzEntity.getJobName();
        String triggerName = quartzEntity.getTriggerName();
        // 初始化定时任务调度器
        Scheduler scheduler;
        // 初始化响应信息为成功
        QuartzResp resp = new QuartzResp(QuartzConst.SUCCESS);
        try {
            // 从取出定时任务调度器
            scheduler = JobUtil.getScheduler();
            // Job名称重复校验
            List<String> jobGroupNames = scheduler.getJobGroupNames();
            // 判断名称中是否包含当前的任务名称
            if (jobGroupNames.contains(jobName)) {
                // 如果存在则设置为失败
                resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.CHECK_ERROR_JOBNAME_EXIST);
            }
            // 触发器名称重复校验
            List<String> triggerGroupNames = scheduler.getTriggerGroupNames();
            // 判断名称中是否包含当前的触发器名称
            if (triggerGroupNames.contains(triggerName)) {
                // 如果存在则设置为失败
                resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.CHECK_ERROR_TRIGGERNAME_EXIST);
            }
        } catch (SchedulerException e) {
            // 出现异常则返回
            resp = new QuartzResp(QuartzConst.ERROR, QuartzConst.CHECK_ERROR_COMMON);
            // 打印错误信息
            if (log.isErrorEnabled()) {
                log.error(e.toString());
            }
        }
        return resp;
    }

    /**
     * 执行保存的业务逻辑
     *
     * @param quartzEntity 定时任务实体
     * @throws ClassNotFoundException 类无法找到异常
     * @throws SchedulerException     定时任务异常
     * @throws QuartzException        自定义异常
     */
    private void doSave(QuartzEntity quartzEntity) throws ClassNotFoundException, QuartzException, SchedulerException {
        // 获取任务名称
        String jobName = quartzEntity.getJobName();
        // 获取任务所属组
        String jobGroup = quartzEntity.getJobGroup();
        // 获取任务触发器名称
        String triggerName = quartzEntity.getTriggerName();
        // 获取任务触发器组名称
        String triggerGroup = quartzEntity.getTriggerGroup();
        // 获取任务实现类名称
        String jobClassName = quartzEntity.getJobClassName();
        // 获取任务描述信息
        String jobDescription = quartzEntity.getJobDescription();
        // 获取定时任务表达式
        String cronExpression = quartzEntity.getCronExpression();
        // 获取任务开始时间
        Date start = JobUtil.paraseToDate(quartzEntity.getStartTime());
        // 获取任务结束时间
        Date end = JobUtil.paraseToDate(quartzEntity.getEndTime());
        // 从调度器工厂中取出定时任务调度器
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        // 创建任务明细，指定需要执行的定时任务类名
        @SuppressWarnings("unchecked")
        JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobClassName.trim()))
                // 设置上任务描述
                .withDescription(jobDescription)
                // 设置上任务的身份信息
                .withIdentity(jobName, jobGroup)
                // 构建这个任务
                .build();
        // 创建触发器构造器，设置触发器的基本信息
        TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger()
                // 设置触发器要执行的任务
                .forJob(jobDetail)
                // 设置触发器名称和组名称
                .withIdentity(TriggerKey.triggerKey(triggerName, triggerGroup))
                // 设置触发器使用的 定时任务表达式
                .withSchedule(cronSchedule(cronExpression));
        // 根据不同的开始和结束时间进行不同的处理
        if (start == null && end == null) {
            // 如果任务的开始和结束时间都为空
            throw new QuartzException(QuartzConst.QUARTZ_ERROR_TIME);
        } else if (end == null) {
            // 如果没有指定结束时间则从起始时间开始执行下去
            triggerBuilder.startAt(start);
        } else if (start == null) {
            // 如果没有指定开始时间则一直运行到指定的结束时间
            triggerBuilder.endAt(end);
        }
        // 创建触发器
        Trigger trigger = triggerBuilder.build();
        // 判断当前指定 任务组的 任务 是否存在
        if (scheduler.checkExists(JobKey.jobKey(jobName, jobGroup))) {
            throw new QuartzException("任务组" + jobGroup + "中，名称为" + jobName + "的任务已经存在");
        }
        // 不存在则创建，并指定使用的触发器
        scheduler.scheduleJob(jobDetail, trigger);
    }

    /**
     * 执行暂停的业务逻辑
     *
     * @param quartzEntity 定时任务实体
     * @throws SchedulerException 计划任务异常
     */
    private void doPause(QuartzEntity quartzEntity) throws SchedulerException {
        // 获取任务的名称
        String jobName = quartzEntity.getJobName();
        // 获取任务组的名称
        String jobGroup = quartzEntity.getJobGroup();
        // 从调度器工厂中取出定时任务调度器
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        // 调用暂停方法
        scheduler.pauseJob(new JobKey(jobName, jobGroup));
    }

    /**
     * 执行恢复的业务逻辑
     *
     * @param quartzEntity 定时任务实体
     * @throws SchedulerException 计划任务异常
     */
    private void doResume(QuartzEntity quartzEntity) throws SchedulerException {
        // 获取任务的名称
        String jobName = quartzEntity.getJobName();
        // 获取任务组的名称
        String jobGroup = quartzEntity.getJobGroup();
        // 从调度器工厂中取出定时任务调度器
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        // 调用恢复任务
        scheduler.resumeJob(new JobKey(jobName, jobGroup));
    }

    /**
     * 执行删除的业务逻辑
     *
     * @param quartzEntity 定时任务实体
     * @throws SchedulerException 计划任务异常
     */
    private void doDelete(QuartzEntity quartzEntity) throws SchedulerException {
        // 获取任务的名称
        String jobName = quartzEntity.getJobName();
        // 获取任务组的名称
        String jobGroup = quartzEntity.getJobGroup();
        // 获取触发器的名称
        String triggerName = quartzEntity.getTriggerName();
        // 获取触发器组的名称
        String triggerGroup = quartzEntity.getTriggerGroup();
        // 从调度器工厂中取出定时任务调度器
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        // 暂停触发器
        scheduler.pauseTrigger(new TriggerKey(triggerName, triggerGroup));
        // 解除指定触发器的定时任务
        scheduler.unscheduleJob(new TriggerKey(triggerName, triggerGroup));
        // 调用删除任务
        scheduler.deleteJob(new JobKey(jobName, jobGroup));
    }
}